#!/usr/bin/env python3
# -*- coding: utf-8 -*-

r'''
codePKu Python helper
created by leetom.
'''

import sys
import ssl
import traceback

import tkinter as tk
from tkinter import ttk
import threading

import os, io, json, subprocess, tempfile
from urllib import parse
from wsgiref.simple_server import make_server, WSGIRequestHandler



# save the web server thread
webserver_thread = None

# now we create GUI
root = tk.Tk()
root.title('编玩边学Python助手')

def quit2():
    global root, webserver_thread
    root.destroy()
    webserver_thread.close()
    sys.exit(0)


root.wm_protocol('WM_DELETE_WINDOW', quit2)
root.createcommand('tk::mac::Quit', quit2)
# root.bind('<Command-Q>', quit)

text = tk.Text(root, width=100, height=30)
text.pack(padx=5, pady=2)

def tprint(sstr):
    text.insert(tk.INSERT, sstr + "\n")

# check version first
def check_version():
    v = sys.version_info
    if v.major == 3 and v.minor >= 4:
        return True
    tprint('Your current python is %d.%d. Please use Python 3.4.' % (v.major, v.minor))
    return False

if not check_version():
    exit(1)

# here is the constants
EXEC = sys.executable
PORT = 26337
TEMP = tempfile.mkdtemp(suffix='_py', prefix='learn_python_')
INDEX = 0

crtFile = "https://www.codepku.com/misc/local.codepku/local.crt"
keyFile = "https://www.codepku.com/misc/local.codepku/local.key"
infoFile = "https://www.codepku.com/misc/local.codepku/python.txt"

def downloadCertFiles():
    import requests
    r = requests.get(crtFile)
    crtPath = os.path.join(TEMP, 'local.crt')
    keyPath = os.path.join(TEMP, 'local.key')
    with open (crtPath, 'wb') as f:
        f.write(r.content)
    tprint("save crt file to %s" % crtPath)
    r = requests.get(keyFile)
    with open(keyPath, 'wb') as f:
        f.write(r.content)
    tprint("save key file to %s" % keyPath)

    return (crtPath, keyPath)

def getHelperInfo():
    import requests
    r = requests.get(infoFile)
    r.encoding = 'utf-8'
    return r.text


# here we extends WSGIRequestHandler and redirect log to tk.Text ignore for now
class PKWSGIRequestHandler(WSGIRequestHandler):
    def log_message(self, format, *args):
        pass

# web server thread
class WebServerThread(threading.Thread):
    # thread 相关代码
    def __init__(self, threadID, name):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name

    def run(self):
        try:
            (cert, key) = downloadCertFiles()
            tprint(getHelperInfo())
        except Exception as err:
            tprint("获取证书失败，请确认已经连接到互联网" + str(err))
            return 0
        try:
            self.httpd = make_server('127.0.0.1', PORT, self.application, handler_class=PKWSGIRequestHandler)
            self.httpd.socket = ssl.wrap_socket(self.httpd.socket, keyfile=key, certfile=cert, server_side=True)
            tprint('编玩边学Python助手正在监听端口 %d \n 请到 https://www.codepku.com/ 进行编程...' % PORT)
            self.httpd.serve_forever()
        except Exception as err:
            tprint(traceback.format_exc())
            tprint('Python助手启动失败，请关闭其他助手或任何占用 %d 端口的程序，或者重新启动电脑后尝试' % PORT + str(err))

    def close(self):
        if hasattr(self, 'httpd'):
            self.httpd.shutdown()

    # 下面是 web server相关代码
    def get_name(self):
        global INDEX
        INDEX = INDEX + 1
        return 'test_%d' % INDEX

    def write_py(self, name, code):
        fpath = os.path.join(TEMP, '%s.py' % name)
        with open(fpath, 'w', encoding='utf-8') as f:
            f.write(code)
        tprint('Code wrote to: %s' % fpath)
        return fpath

    def decode(self, s):
        try:
            return s.decode('utf-8')
        except UnicodeDecodeError:
            return s.decode('gbk')

    def application(self, environ, start_response):
        host = environ.get('HTTP_HOST')
        method = environ.get('REQUEST_METHOD')
        path = environ.get('PATH_INFO')

        # 跨域调用，OPTIONS应该返回允许的域名
        if method == 'OPTIONS' and path == "/run":
            headers = [('Content-Type', 'application/json')]
            headers.append(('Access-Control-Allow-Origin', '*'))
            headers.append(('Access-Control-Allow-Headers', 'X-Requested-With, Content-type, X-CSRF-Token'))
            headers.append(('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'))
            headers.append(('Allow', 'POST, HEAD, OPTIONS, GET'))
            start_response('200 OK', headers)
            return [b'1']   #随便返回一个值。主要是需要Headers
        # 请求是否合法
        if method != 'POST' or path != '/run' or not environ.get('CONTENT_TYPE', '').lower().startswith('application/x-www-form-urlencoded'):
            start_response('400 Bad Request', [('Content-Type', 'application/json')])
            return [b'{"error":"bad_request"}']

        #判断是否提交了代码 
        s = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
        qs = parse.parse_qs(s.decode('utf-8'))
        if not 'code' in qs:
            start_response('400 Bad Request', [('Content-Type', 'application/json')])
            return [b'{"error":"invalid_params"}']

        #校验完毕，开始保存并运行程序
        name = qs['name'][0] if 'name' in qs else self.get_name()
        code = qs['code'][0]
        headers = [('Content-Type', 'application/json')]
        origin = environ.get('HTTP_ORIGIN', '')
        headers.append(('Access-Control-Allow-Origin', origin))
        start_response('200 OK', headers)
        r = dict()
        try:
            fpath = self.write_py(name, code)
            tprint('Execute: %s %s' % (EXEC, fpath))
            # subprocess.STDOUT = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)
            output = self.decode(subprocess.check_output([EXEC, fpath], stderr=subprocess.STDOUT))
            r['output'] = output
            r['error'] = ''
        except subprocess.CalledProcessError as e:
            r = dict(error='Exception', output=self.decode(e.output))
        except subprocess.TimeoutExpired as e:
            r = dict(error='Timeout', output='执行超时')
        except subprocess.CalledProcessError as e:
            r = dict(error='Error', output='执行错误')
        tprint('Execute done.')
        #与 runCode统一格式
        res = {'data': r, 'msg': '', 'status': 1}
        return [json.dumps(res).encode('utf-8')]

if __name__ == '__main__':

    webserver_thread = WebServerThread(1, 'Web Thread')
    webserver_thread.start()

    # now we start GUI main loop
    tk.mainloop()

    # here runs when we close it
    webserver_thread.close()
